home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / C / Applications / Eudora 1.3.1 / source / pop.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-03-16  |  28.2 KB  |  1,129 lines  |  [TEXT/MPS ]

  1. #define FILE_NUM 30
  2. /* Copyright (c) 1990-1992 by the University of Illinois Board of Trustees */
  3. /************************************************************************
  4.  * functions for dealing with a pop 2 server
  5.  ************************************************************************/
  6. #pragma load EUDORA_LOAD
  7. #ifdef    KERBEROS
  8. #include                <krb.h>
  9. #endif
  10. #pragma segment POP
  11.  
  12. typedef enum
  13. {
  14.     user=1, pass, stat, retr, dele, quit, top, list, apop
  15. } POPEnum;
  16.  
  17. #define CMD_BUFFER 514
  18. #define PSIZE (UseCTB ? 256 : 4096)
  19. /************************************************************************
  20.  * private routines
  21.  ************************************************************************/
  22. #ifndef KERBEROS
  23. int POPIntroductions(void);
  24. #else
  25. int POPIntroductions(UPtr hostname);
  26. #endif
  27. int POPByeBye(void);
  28. int POPCmd(int cmd, UPtr args);
  29. int POPCmdGetReply(int cmd, UPtr args, UPtr buffer, long *size);
  30. int StartPOP(UPtr serverName, short port);
  31. int EndPOP(void);
  32. int POPGetMessage(long messageNumber,short *gotSome);
  33. int POPCmdError(int cmd,UPtr args,UPtr message);
  34. int PutOutFromLine(short refN,long *fromLen);
  35. int DupHeader(short refN,UPtr buff,long bSize,long offset,long headerSize);
  36. int SplitAndSave(short refN,long estSize);
  37. int WritePartLine(short refN,short partNo);
  38. short DoBody(short refN,long headerSize,long origOffset,char *buf,long bSize,Boolean abomination,long estSize);
  39. int Prr;
  40. Boolean PopConnected;
  41. int FirstUnread(int count);
  42. Boolean HasBeenRead(short msgNum,short count);
  43. void StampPartNumber(MSumPtr sum,short part,short count);
  44. UPtr ExtractStamp(UPtr stamp,UPtr banner);
  45. Boolean GenDigest(UPtr banner,UPtr secret,UPtr digest);
  46.  
  47. /************************************************************************
  48.  * public routines
  49.  ************************************************************************/
  50. /************************************************************************
  51.  * GetMyMail - the biggie; transfers mail into In mailbox
  52.  ************************************************************************/
  53. short GetMyMail(Boolean quietly)
  54. {
  55. #pragma unused(quietly)
  56.     int messageCount;
  57.     int message,first;
  58.     Str255 scratch;
  59.     Str255 msgname;
  60.     Str63 hostName;
  61.     short port;
  62.     short gotSome = 0;
  63.     TOCHandle tocH;
  64.     short err;
  65.     
  66.     GetPOPInfo(msgname,hostName);
  67.     port = GetRLong(POP_PORT);
  68.     if (StartPOP(hostName,port)==noErr)
  69.     {
  70. #ifdef    KERBEROS
  71.         messageCount = POPIntroductions(hostName);
  72. #else
  73.         messageCount = POPIntroductions();
  74. #endif
  75.         if (messageCount>0 && !Prr)
  76.         {
  77.             ComposeLogR(LOG_RETR,nil,START_POP_LOG,hostName,port,messageCount);
  78.             if (tocH=GetInTOC())
  79.             {
  80.                 for (first=message=PrefIsSet(PREF_DONT_DELETE) ?
  81.                                                                                         FirstUnread(messageCount) : 1;
  82.                             message<=messageCount;
  83.                             message++)
  84.                 {
  85.                     ComposeRString(msgname,POP_STATUS_FMT,
  86.                                                     message-first+1,messageCount-first+1);
  87.                     Progress(0,msgname);
  88.                     (*tocH)->dirty = True;
  89.                     if (POPGetMessage(message,&gotSome)) break;
  90.                 }
  91.             }
  92.             else
  93.                 Prr = 1;
  94.         }
  95.     }
  96.     if (AttachedFiles) SetHandleBig(AttachedFiles,0);
  97.     err = Prr;
  98.     EndPOP();
  99.     
  100. #ifndef KERBEROS
  101.     if (PrefIsSet(PREF_SAVE_PASSWORD) && *Password &&
  102.         (!EqualString(Password,GetPref(scratch,PREF_PASS_TEXT),True,True) ||
  103.          !EqualString(SecondPass,GetPref(scratch,PREF_AUXPW),True,True)))
  104.     {
  105.         ChangeStrn(PREF_STRN,PREF_PASS_TEXT,Password);
  106.         ChangeStrn(PREF_STRN,PREF_AUXPW,SecondPass);
  107.         UpdateResFile(SettingsRefN);
  108.     }
  109. #endif
  110.     return(gotSome);
  111. }
  112.  
  113. /************************************************************************
  114.  * POPrror - see if there was a POP error
  115.  ************************************************************************/
  116. int POPrror(void)
  117. {
  118.     return(Prr);
  119. }
  120.     
  121. /************************************************************************
  122.  * private routines
  123.  ************************************************************************/
  124. /************************************************************************
  125.  * StartPOP - get connected to the POP server
  126.  ************************************************************************/
  127. int StartPOP(UPtr serverName, short port)
  128. {
  129.     PopConnected=False;
  130.     Prr=ConnectTrans(serverName,port,False);
  131.     return(Prr);
  132. }
  133.  
  134. /************************************************************************
  135.  * EndPOP - get rid of the POP server
  136.  ************************************************************************/
  137. int EndPOP()
  138. {
  139.     SilenceTrans(True);
  140.     if (!Prr && !CommandPeriod)
  141.     {
  142.         (void) POPByeBye();
  143.         Prr=DisTrans();
  144.     }
  145.     Prr = DestroyTrans();
  146.     return(Prr);
  147. }
  148.  
  149. #ifdef    KERBEROS
  150.                 /* callback fns kerberos uses to read/write on our connection */
  151. static int            kread(char *buf, int len, void *uptr)
  152. {
  153.                 int err;
  154.                 int ret_len = len;
  155.                 
  156.                 err = TCPRecvTrans((UPtr) buf, &ret_len);
  157.                 if (!err)
  158.                                 return len;
  159.                 else
  160.                                 return 0;
  161.                 }
  162. static int            kwrite(char *buf, int len, void *uptr)
  163. {
  164.                 int err;
  165.  
  166.                 err = TCPSendTrans(1, (UPtr) buf, (int) len);
  167.                 if (!err)
  168.                                 return len;
  169.                 else
  170.                                 return 0;
  171.                 }
  172. #endif
  173.  
  174. /************************************************************************
  175.  * POPIntroductions - sniff the POP server's bottom, and vice-versa
  176.  ************************************************************************/
  177. #ifndef KERBEROS
  178. int POPIntroductions(void)
  179. #else
  180. int POPIntroductions(UPtr hostname)
  181. #endif
  182. {
  183.     Str255 buffer;
  184.     Str63 userName;
  185.     Str255 args;
  186.     long size;
  187.     int result = -1;
  188. #ifdef    KERBEROS
  189.                 int status;
  190.                 KTEXT_ST ticket;
  191.                 long authopts;
  192.                 char name[ANAME_SZ];
  193.                 char instance[INST_SZ];
  194.                 char realm[REALM_SZ];
  195.                 char hname[256];
  196. #else
  197.     Boolean useAPOP = PrefIsSet(PREF_APOP);
  198.     Str255 digest;    
  199. #endif
  200.                 
  201. #ifdef    KERBEROS
  202.                 Progress(NoChange,GetRString(buffer,LOGGING_IN));
  203.                 GetPOPInfo(userName,args);
  204.                 
  205.                 PtoCcpy(hname, hostname);
  206.                 PtoCcpy(name, userName);
  207.                 instance[0] = 0;
  208.                 realm[0] = 0;
  209.                 if(0 != (status = krb_get_lrealm(realm, 1))) {
  210.                                 char        message[256];
  211.                                 
  212.                                 sprintf(message, "Login failed: %s", krb_err_txt[status]);
  213.                                 c2pstr(message);
  214.                                 AlertStr(OK_ALRT,Caution,message);
  215.                                 Prr = '-';
  216.                                 goto done;
  217.                 }
  218.                 
  219.                 authopts = 0L;
  220.                 status = krb_sendauth(authopts, &ticket, "pop", hname, NULL, NULL,
  221.                                                                                                                 NULL, NULL, NULL, NULL, NULL, NULL, NULL, "VERSION9",
  222.                                                                                                                 kread, kwrite, NULL, name, instance, realm);
  223.                 if (status != KSUCCESS) {
  224.                                 char        message[256];
  225.                                 
  226.                                 sprintf(message, "Login failed: %s", krb_err_txt[status]);
  227.                                 c2pstr(message);
  228.                                 AlertStr(OK_ALRT,Caution,message);
  229.                                 Prr = '-';
  230.                                 goto done;
  231.                 }
  232. #endif    
  233.  
  234.     do
  235.     {
  236.         size = sizeof(buffer)-1;
  237.         Prr = RecvLine(buffer+1,&size);
  238.         if (Prr) goto done;
  239.         buffer[0] = MIN(size,127);
  240.         Progress(NoChange,buffer);
  241.     }
  242.     while (buffer[1]!='+' && buffer[1]!='-');
  243.     
  244.     PopConnected = size && (buffer[1]=='+' || buffer[1]=='-');
  245.     if (buffer[1] != '+')
  246.     {
  247.         Prr = buffer[1];
  248.         POPCmdError(-1,nil,buffer);
  249.         goto done;
  250.     }
  251.     
  252.     if (useAPOP) {useAPOP = GenDigest(buffer,Password,digest);}
  253.     
  254. #ifndef KERBEROS
  255.     Progress(NoChange,GetRString(buffer,LOGGING_IN));
  256.     GetPOPInfo(userName,args);
  257. #endif
  258.  
  259.     if (useAPOP)
  260.     {
  261.         size = sizeof(buffer)-1;
  262.         PCopy(args,userName);
  263.         PCatC(args,' ');
  264.         PCat(args,digest);
  265.         Prr = POPCmdGetReply(apop,args,buffer,&size);
  266.     }
  267.     else
  268.     {
  269.         size = sizeof(buffer)-1;
  270.         Prr = POPCmdGetReply(user,userName,buffer,&size);
  271.         if (Prr || *buffer != '+')
  272.         {
  273.             if (!Prr) POPCmdError(user,userName,buffer);
  274.             Prr = '-';
  275.             goto done;
  276.         }
  277.     
  278. #ifndef KERBEROS
  279.         PCopy(args,Password);
  280. #else
  281.         PCopy(args, "\parbitrary");
  282. #endif
  283.     
  284.         size = sizeof(buffer)-1;
  285.         Prr = POPCmdGetReply(pass,args,buffer,&size);
  286.     }
  287.     if (Prr || *buffer != '+')
  288.     {
  289.         if (!Prr)
  290.         {
  291.             POPSecure = False;
  292.             POPCmdError(pass,nil,buffer);
  293. #ifndef KERBEROS
  294.             InvalidatePasswords(False,True);
  295. #endif
  296.         }
  297.         Prr = '-';
  298.         goto done;
  299.     }
  300.     POPSecure = True;
  301.     
  302. #ifndef POPSECURE
  303.     Progress(NoChange,GetRString(buffer,LOOK_MAIL));
  304. #endif
  305.     size = sizeof(buffer)-1;
  306.     Prr = POPCmdGetReply(stat,nil,buffer,&size);
  307.     if (Prr || *buffer != '+')
  308.     {
  309.         if (!Prr)  POPCmdError(stat,nil,buffer);
  310.         Prr = '-';
  311.         goto done;
  312.     }
  313.     
  314.     result = atoi(buffer+3);
  315.     done:
  316.     return(result);
  317. }
  318.  
  319. /************************************************************************
  320.  * POPByeBye - tell the POP server we're leaving
  321.  ************************************************************************/
  322. int POPByeBye(void)
  323. {
  324.     char buffer[CMD_BUFFER];
  325.     long size=sizeof(buffer);
  326.     if (!PopConnected) return(noErr);
  327.     Prr = POPCmdGetReply(quit,nil,buffer,&size);
  328.     return (Prr || *buffer != '+');
  329. }
  330.  
  331. /************************************************************************
  332.  * POPCmd - Send a command to the POP server
  333.  ************************************************************************/
  334. int POPCmd(int cmd, UPtr args)
  335. {
  336.     char buffer[CMD_BUFFER];
  337.  
  338.     GetRString(buffer,POP_STRN+cmd);
  339.     if (args && *args)
  340.         PCat(buffer,args);
  341. #ifdef DEBUG_COMM
  342.     Progress(NoBar,buffer);
  343. #endif DEBUG_COMM
  344.     PCat(buffer,NewLine);
  345.     
  346.     Prr=SendTrans(1,buffer+1,*buffer);
  347.     
  348.     return(Prr);
  349. }
  350.  
  351. /************************************************************************
  352.  * POPCmdGetReply - send a POP command and get a reply
  353.  ************************************************************************/
  354. int POPCmdGetReply(int cmd, UPtr args, UPtr buffer, long *size)
  355.     long rSize;
  356.     if (Prr=POPCmd(cmd,args)) return(Prr);                /* error in transmission */
  357.     do
  358.     {
  359.         rSize = *size;
  360.         Prr = RecvLine(buffer,&rSize);
  361.     }
  362.     while (!Prr && *buffer!='+' && *buffer!='-');
  363.     *size = rSize;
  364.     return(Prr);
  365. }
  366.  
  367. /************************************************************************
  368.  * POPGetMessage - get a message from the POP server
  369.  ************************************************************************/
  370. int POPGetMessage(long messageNumber,short *gotSome)
  371. {
  372.     Str255 args;
  373.     char buffer[CMD_BUFFER];
  374.     long size = sizeof(buffer);
  375.     long msgsize=0;
  376.     char *spot;
  377.     short count;
  378.         
  379.     NumToString((long)messageNumber,args);
  380.     
  381.     if (Prr=POPCmdGetReply(list,args,buffer,&size)) return(Prr);
  382.     if (*buffer != '+')
  383.     {
  384.         Prr = *buffer;
  385.         POPCmdError(list,args,buffer);
  386.         return(Prr);
  387.     }
  388.     strtok(buffer," ");                                                                     /* skip ok */
  389.     strtok(nil," ");                                                                            /* skip message id */
  390.     if (spot=strtok(nil," \n")) msgsize = atoi(spot);         /* read message size */
  391.     
  392.   size = sizeof(buffer);
  393.     if (PrefIsSet(PREF_NO_BIGGIES) && msgsize>GetRLong(BIG_MESSAGE))
  394.     {
  395.       NumToString(GetRLong(BIG_MESSAGE_FRAGMENT),buffer);
  396.         PCatC(args,' ');
  397.         PCat(args,buffer);
  398.         msgsize = -1;    /* let everyone down the line know what's going down */
  399.         Prr = POPCmdGetReply(top,args,buffer,&size);
  400.         NoAttachments = True;    /* don't do BinHex */
  401.     }
  402.     else
  403.     {
  404.       Prr=POPCmdGetReply(retr,args,buffer,&size);
  405.         NoAttachments = False;
  406.     }
  407.     if (Prr) return(Prr);
  408.     if (*buffer!='+')
  409.     {
  410.         POPCmdError(retr,args,buffer);
  411.         return(1);
  412.     }
  413.     BadBinHex = False;
  414.     BeginHexBin();
  415.     if (!AttachedFiles) AttachedFiles=NuHandle(0);
  416.     SetHandleBig(AttachedFiles,0);
  417.     count=POPMessageBody(msgsize);
  418.     SetHandleBig(AttachedFiles,0);
  419.     EndHexBin();
  420.     SaveAbomination(nil,0);
  421.     if (!Prr && !CommandPeriod && !PrefIsSet(PREF_DONT_DELETE) && !NoAttachments &&
  422.         (!BadBinHex || ReallyDoAnAlert(BAD_HEXBIN_ALRT,Stop)==BAD_HEXBIN_IGNORE))
  423.     {
  424.         NumToString(messageNumber,args);
  425.         size = sizeof(buffer);
  426.         Prr=POPCmdGetReply(dele,args,buffer,&size);
  427.     }
  428.  
  429.     if (!Prr) (*gotSome)+=count;
  430.     return(Prr);
  431. }
  432.  
  433. /************************************************************************
  434.  * POPCmdError - report an error for an POP command
  435.  ************************************************************************/
  436. int POPCmdError(int cmd, UPtr args, UPtr message)
  437. {
  438.     Str255 theCmd;
  439.     Str255 theError;
  440.     int err;
  441.  
  442.     GetRString(theCmd,POP_STRN+cmd);
  443.     if (args && *args)
  444.         PCat(theCmd,args);
  445.     strcpy(theError+1,message);
  446.     *theError = strlen(theError+1);
  447.     if (theError[*theError]=='\012') (*theError)--;
  448.     if (theError[*theError]=='\015') (*theError)--;
  449.     MyParamText(theCmd,theError,"\pPOP","");
  450.     err = ReallyDoAnAlert(PROTO_ERR_ALRT,Note);
  451.     return(err);
  452. }
  453.  
  454. /************************************************************************
  455.  * POPMessageBody - read in the body of a message
  456.  ************************************************************************/
  457. int POPMessageBody(long estSize)
  458. {
  459.     UPtr text=nil;
  460.     TOCType **tocH;
  461.     MSumType sum;
  462.     long eof,chopHere;
  463.     Str255 name;
  464.     short count=0,part;
  465.     
  466.     /*
  467.      * make the message summary
  468.      */
  469.     WriteZero(&sum,sizeof(MSumType));
  470.  
  471.     /*
  472.      * grab the "In" box
  473.      */
  474.     if (!(tocH=GetInTOC()))
  475.         return(1);
  476.     PCopy(name,(*tocH)->name);
  477.     
  478.     Prr = BoxFOpen(tocH);
  479.     if (Prr) {FileSystemError(OPEN_MBOX,name,Prr); goto done;}
  480.  
  481.     eof = FindTOCSpot(tocH,estSize);
  482.  
  483.     Prr = SetFPos((*tocH)->refN, fsFromStart, eof);
  484.     if (Prr) {FileSystemError(WRITE_MBOX,name,Prr); goto done;}
  485.         
  486.     count = SplitAndSave((*tocH)->refN,estSize);
  487.     
  488. done:
  489.     if (!Prr && !GetFPos((*tocH)->refN,&chopHere))
  490.         SetEOF((*tocH)->refN,chopHere);
  491.     
  492.     Prr = BoxFClose(tocH) || Prr;
  493.     if (Prr || !count) return(0);
  494.     
  495.     /*
  496.      * now, read it back from the file
  497.      */ 
  498.     if (Prr=OpenLine((*tocH)->vRef,(*tocH)->dirId,name))
  499.          {FileSystemError(READ_MBOX,name,Prr);return(0);}
  500.     if (Prr=SeekLine(eof)) return(FileSystemError(READ_MBOX,name,Prr));
  501.     
  502.     ReadSum(nil,False);
  503.     for (part=1;part<=count;part++)
  504.     {
  505.         Prr = !ReadSum(&sum,False);
  506.         TransLitString(sum.from);
  507.         TransLitString(sum.subj);
  508.         if (!sum.seconds) sum.seconds = GMTDateTime();
  509.         if (count>1) StampPartNumber(&sum,part,count);
  510.         if (Prr || (Prr=!SaveMessageSum(&sum,tocH))) break;
  511.     }
  512.     ReadSum(nil,False);
  513.             
  514.     CloseLine();
  515.     if (Prr)
  516.     {
  517.         WarnUser(READ_MBOX,Prr);
  518.         return(0);
  519.     }
  520.  
  521.     InvalSum(tocH,(*tocH)->count-1);
  522.     if (!PrefIsSet(PREF_CORVAIR))
  523.     {
  524.         Prr = WriteTOC(tocH);
  525.         FlushVol(nil,MyVRef);
  526.     }
  527.     MakeMessTitle(name,tocH,(*tocH)->count-count);
  528.     ComposeLogR(LOG_RETR,nil,MSG_GOT,name,count);
  529.   return(count);
  530. }
  531.  
  532. /************************************************************************
  533.  * SplitAndSave - read a message, (possibly) splitting it into parts and
  534.  * saving it.
  535.  ************************************************************************/
  536. int SplitAndSave(short refN,long estSize)
  537. {
  538.     long origOffset;
  539.     long headerSize;
  540.     long lastProgress=0;
  541.     Str255 buf;
  542.     long size;
  543.     Boolean wasNl = False;
  544.     UPtr writeHere, writeEnd;
  545.     short count=0;
  546.     Str127 abomination;
  547.     Boolean abominable=False;
  548.     Str31 status;
  549.     Boolean foundStatus = False;
  550.     
  551.     if (estSize < 0) GetRString(status,STATUS);
  552.     
  553.     GetRString(abomination,ABOMINATION);
  554.     if (estSize>0) ByteProgress(nil,0,estSize);
  555.     GetFPos(refN,&origOffset);
  556.     
  557.     if (Prr=PutOutFromLine(refN,&headerSize)) return(Prr);
  558.     /*
  559.      * read the header
  560.      */
  561.     for (size=sizeof(buf)-1; !(Prr=RecvLine(buf+1,&size)); size=sizeof(buf)-1)
  562.     {
  563.         writeHere = buf+1;
  564.         writeEnd = writeHere+size-1;
  565.  
  566.         /*
  567.          * check for embedded From lines
  568.          */
  569.         if (IsFromLine(buf+1)){long one=1;(void)FSTabWrite(refN,&one,">");}
  570.         
  571.         /*
  572.          * check for abominations
  573.          */
  574.         if (!striscmp(abomination+1,buf+1)) abominable=True;
  575.         /*
  576.          * progress
  577.          */
  578.         if ((lastProgress+=size) > PSIZE)
  579.         {
  580.             if (estSize) ByteProgress(nil,-lastProgress,0);
  581.             lastProgress = 0;
  582.         }
  583.                 
  584.         /*
  585.          * check for end of message
  586.          */
  587.         if (wasNl && size==2 && buf[1]=='.')
  588.         {
  589.             if (Prr=FSTabWrite(refN,&size,"\n\n")) FileSystemError(WRITE_MBOX,"",Prr);
  590.             goto done;
  591.         }
  592.  
  593.         /*
  594.          * check for escaped periods
  595.          */
  596.         if (wasNl && buf[1]=='.')
  597.             writeHere++;
  598.  
  599.         /*
  600.          * check for continuations, but only if the last "line" ended with
  601.          * a newline; if it didn't, we're just continuing anyway
  602.          */
  603.         if (wasNl)
  604.             if (size>1 && IsWhite(buf[1]))
  605.             {
  606.                 while(IsWhite(*writeHere)) writeHere++; /* skip whitespace */
  607.                 writeHere--;                                        /* well, not ALL of it... */
  608.             }
  609.             else
  610.                 *--writeHere = '\n';        /* we need a newline for the previous line */
  611.         
  612.         /*
  613.          * remove a trailing newline, in case the next line is a continuation
  614.          * (and curse RFC 822)
  615.          */
  616.         if (wasNl= *writeEnd=='\n') writeEnd--;
  617.         
  618.         /*
  619.          * is it a status header?  Do we care?
  620.          */
  621.         if (estSize < 0 && wasNl && !foundStatus && writeHere < writeEnd)
  622.         {
  623.             writeEnd[1] = 0;
  624.             foundStatus = !striscmp(writeHere+1,status+1);
  625.         }
  626.         
  627.         /*
  628.          * now we can write it out...
  629.          */
  630.         size = writeEnd-writeHere+1;
  631.         if (Prr=FSTabWrite(refN,&size,writeHere))
  632.         {
  633.             FileSystemError(WRITE_MBOX,"",Prr);
  634.             goto done;
  635.         }
  636.         
  637.         /*
  638.          * was that the end of the header, by the way?
  639.          */
  640.         if (size==1 && *writeHere=='\n' ||
  641.                 size==2 && *writeEnd=='\n' && *writeHere=='\n')
  642.         {
  643.             size = 1;
  644.             if (Prr=FSTabWrite(refN,&size,"\n"))
  645.             {
  646.                 FileSystemError(WRITE_MBOX,"",Prr);
  647.                 goto done;
  648.             }
  649.             break;
  650.         }
  651.     }
  652.     
  653.     /*
  654.      * at this point, we've saved the header (good for us, eh?)
  655.      * now, go save the body
  656.      */
  657.     GetFPos(refN,&headerSize);
  658.     count=DoBody(refN,headerSize-origOffset,origOffset,buf,sizeof(buf),abominable,estSize);
  659. done:
  660.     return(foundStatus ? 0 : count);
  661.  
  662. /************************************************************************
  663.  * DoBody - read the body of a message from a pop-3 server
  664.  ************************************************************************/
  665. short DoBody(short refN,long headerSize,long origOffset,char *buf,long bSize,Boolean abomination,long estSize)
  666. {
  667.     long writeCount;
  668.     Str63 beginHex;
  669.     UPtr writeHere,writeEnd;
  670.     HexBinStates amHexing = HexDone;
  671.     AbStates snowMan = AbDone;
  672.     Boolean wasNl = True;
  673.     long messageSize=headerSize;
  674.     long lastProgress=0;
  675.     long fragSize=GetRLong(FRAGMENT_SIZE);
  676.     long splitThresh=GetRLong(SPLIT_THRESH);
  677.     long hexOffset;
  678.     short partNo = 0;
  679.     long size;
  680.     long oTicks = TickCount();
  681.  
  682.     GetRString(beginHex,BINHEX);    
  683.     for (size=bSize; !(Prr=RecvLine(buf,&size)); size=bSize)
  684.     {
  685.         writeHere = buf;
  686.         writeEnd = buf + size-1;
  687.         
  688.         /*
  689.          * check for embedded From lines
  690.          */
  691.         if (IsFromLine(buf)){long one=1;(void)FSTabWrite(refN,&one,">");}
  692.         
  693.         /*
  694.          * progress
  695.          */
  696.         if ((lastProgress+=size) > PSIZE)
  697.         {
  698.             if (estSize>0 || amHexing!=HexDone || snowMan!=AbDone)
  699.                 ByteProgress(nil,-lastProgress,0);
  700.             lastProgress = 0;
  701.         }
  702.         
  703.         /*
  704.          * check for end of message
  705.          */
  706.         if (wasNl && size==2 && writeHere[0]=='.') break;
  707.  
  708.         /*
  709.          * check for periods
  710.          */
  711.         if (wasNl && writeHere[0]=='.')
  712.             writeHere++;
  713.             
  714.         /*
  715.          * does this line end in a newline?
  716.          */
  717.         wasNl = *writeEnd=='\n';
  718.         
  719.         /*
  720.          * do some autohexing
  721.          */
  722.         if (!NoAttachments) switch (amHexing)
  723.         {
  724.             case HexDone:
  725.                 if (size>=*beginHex && !strncmp(beginHex+1,writeHere,*beginHex))
  726.                 {
  727.                     amHexing = NotHex;
  728.                     GetFPos(refN,&hexOffset);  /* save binhex start */
  729.                     DontTranslate = True;
  730.                 }
  731.                 break;
  732.             case NotHex:
  733.             case CollectName:
  734.             case CollectInfo:
  735.                 amHexing = SaveHexBin(writeHere,writeEnd-writeHere+1,estSize);
  736.                 if (amHexing>CollectInfo && amHexing!=HexDone)
  737.                 {
  738.                     SetFPos(refN,fsFromStart,hexOffset);    /* toss the saved bits */
  739.                     SetEOF(refN,hexOffset);
  740.                     goto loop;    /* do NOT save the line into the message proper */
  741.                 }
  742.                 break;
  743.             default:
  744.                 amHexing = SaveHexBin(writeHere,writeEnd-writeHere+1,estSize);
  745.                 goto loop;    /* do NOT save the line into the message proper */
  746.         }
  747.         
  748.         if (!NoAttachments && abomination)
  749.             switch(snowMan)
  750.                     
  751.             {
  752.                 case AbDone:
  753.                     if (IsAbLine(writeHere,size))
  754.                     {
  755.                         snowMan = NotAb;
  756.                         GetFPos(refN,&hexOffset);  /* save binhex start */
  757.                         DontTranslate = True;
  758.                     }
  759.                     break;
  760.                 case AbHeader:
  761.                 case AbName:
  762.                     snowMan = SaveAbomination(writeHere,writeEnd-writeHere+1);
  763.                     if (snowMan>AbName && snowMan!=AbDone)
  764.                     {
  765.                         SetFPos(refN,fsFromStart,hexOffset);    /* toss the saved bits */
  766.                         SetEOF(refN,hexOffset);
  767.                         goto loop;    /* do NOT save the line into the message proper */
  768.                     }
  769.                     break;
  770.                 default:
  771.                     snowMan = SaveAbomination(writeHere,writeEnd-writeHere+1);
  772.                     goto loop;    /* do NOT save the line into the message proper */
  773.             }
  774.         
  775.         /*
  776.          * are we over the limit?
  777.          */
  778.         if ((estSize>splitThresh || estSize<messageSize) && messageSize > fragSize)
  779.         {
  780.             Str255 buf2; short b2size=sizeof(buf2);
  781.             writeCount = 1;     /* make sure we have newline */
  782.             if (Prr=FSTabWrite(refN,&writeCount,"\n")) return(Prr);
  783.             if (Prr=DupHeader(refN,buf2,b2size,origOffset,headerSize)) return(Prr);
  784.             /*if (Prr=WritePartLine(refN,++partNo)) return(Prr);*/
  785.             ++partNo;
  786.             messageSize = headerSize;
  787.         }
  788.         
  789.         /*
  790.          * save the data
  791.          */
  792.         writeCount = writeEnd-writeHere+1;
  793.         if (Prr=FSTabWrite(refN,&writeCount,writeHere))
  794.             return(FileSystemError(WRITE_MBOX,"",Prr));
  795.         messageSize += writeCount;
  796.         loop:
  797.             if (amHexing==HexDone && snowMan==AbDone) DontTranslate = False;
  798.     }
  799.     if (!Prr && (writeCount=GetHandleSize(AttachedFiles)))
  800.     {
  801.         Prr=FSTabWrite(refN,&writeCount,LDRef(AttachedFiles));
  802.         UL(AttachedFiles);
  803.     }
  804.     if (!Prr && estSize < 0)
  805.     {
  806.       Str255 msg;
  807.         GetRString(msg,BIG_MESSAGE_MSG);
  808.         writeCount = *msg;
  809.         Prr=FSTabWrite(refN,&writeCount,msg+1);
  810.     }
  811.   if (Prr) return(FileSystemError(WRITE_MBOX,"",Prr));
  812.     Progress(100,nil);
  813.     return(Prr?partNo : partNo+1);
  814. }
  815.  
  816. /************************************************************************
  817.  *
  818.  ************************************************************************/
  819. int WritePartLine(short refN,short partNo)
  820. {
  821.     Str63 partLine;
  822.     long partLen;
  823.     
  824.     ComposeRString(partLine,PART_FMT,partNo);
  825.     partLen = *partLine;
  826.     if (Prr=FSTabWrite(refN,&partLen,partLine+1))
  827.         return(FileSystemError(WRITE_MBOX,"",Prr));
  828.     return(noErr);
  829. }
  830.  
  831. /************************************************************************
  832.  *
  833.  ************************************************************************/
  834. int PutOutFromLine(short refN,long *fromLen)
  835. {
  836.     Str255 fromLine;
  837.     long len;
  838.     
  839.     *fromLen = len = SumToFrom(nil,fromLine);
  840.     if (Prr=FSTabWrite(refN,&len,fromLine))
  841.         return(FileSystemError(WRITE_MBOX,"",Prr));
  842.     return(noErr);
  843. }
  844.  
  845. /************************************************************************
  846.  *
  847.  ************************************************************************/
  848. int DupHeader(short refN,UPtr buff,long bSize,long offset,long headerSize)
  849. {
  850.     long currentOffset;
  851.     long readBytes,writeBytes;
  852.     long copied;
  853.     
  854.     if (Prr=GetFPos(refN,¤tOffset))
  855.         return(FileSystemError(READ_MBOX,"",Prr));
  856.     for (copied=0; copied<headerSize; copied += readBytes)
  857.     {
  858.         if (Prr=SetFPos(refN,fsFromStart,offset+copied))
  859.             return(FileSystemError(READ_MBOX,"",Prr));
  860.         readBytes = bSize < headerSize-copied ? bSize : headerSize-copied;
  861.         if (Prr=FSRead(refN,&readBytes,buff))
  862.             return(FileSystemError(READ_MBOX,"",Prr));
  863.         if (Prr=SetFPos(refN,fsFromStart,currentOffset))
  864.             return(FileSystemError(WRITE_MBOX,"",Prr));
  865.         writeBytes = readBytes;
  866.         if (Prr=FSZWrite(refN,&writeBytes,buff))
  867.             return(FileSystemError(WRITE_MBOX,"",Prr));
  868.         currentOffset += writeBytes;
  869.     }
  870.     return(noErr);
  871. }
  872.  
  873. /************************************************************************
  874.  * FirstUnread - find the first unread message
  875.  *     We do try to be clever about it.  If 
  876.  ************************************************************************/
  877. int FirstUnread(int count)
  878. {
  879.     short first, last, on;
  880.     static short lastCount=0;
  881.     Boolean hasBeen;
  882.     
  883.     on = lastCount;
  884.     lastCount = count;
  885.  
  886. #define SETHASBEEN(o,c)\
  887.     do {hasBeen=HasBeenRead(o,c);if (CommandPeriod) return(c+1);} while (0) 
  888.     if (PrefIsSet(PREF_NO_BIGGIES))
  889.     {
  890.         /* Heuristics */
  891.         if (on && on<=count)
  892.         {
  893.             SETHASBEEN(on,count);
  894.             if (hasBeen)
  895.             {
  896.                 SETHASBEEN(on+1,count);
  897.                 if (!hasBeen) return(on+1);
  898.             }
  899.         }
  900.         SETHASBEEN(count,count);
  901.         if (hasBeen) return(count+1);
  902.         if (count==1) return(1);
  903.         
  904.         /* search... */
  905.         for (on=count-1;on;on--)
  906.         {
  907.             SETHASBEEN(on,count);
  908.             if (hasBeen) break;
  909.         }
  910.         return(on+1);
  911.     }
  912.     else
  913.     {
  914.         first = 1;
  915.         last = count;
  916.         /*
  917.          * try to cut the search short via heuristics
  918.          */
  919.         if (on && on<=count)
  920.         {
  921.             SETHASBEEN(on,count);
  922.             if (hasBeen)
  923.             {
  924.                 if (on<count)
  925.                 {
  926.                     SETHASBEEN(++on,count);
  927.                     if (!hasBeen) return(on);
  928.                 }
  929.                 first = on+1;
  930.             }
  931.             else
  932.                 last = on-1;
  933.         }
  934.         else
  935.         {
  936.             SETHASBEEN(count,count);
  937.             if (hasBeen) return(count+1);
  938.             SETHASBEEN(1,count);
  939.             if (count==1 || !hasBeen) return(1);
  940.             last=count-1;
  941.             first = 2;
  942.             on = count;
  943.             hasBeen = False;
  944.         }
  945.             
  946.         /*
  947.          * hi ho, hi ho, it's off to search we go
  948.          */
  949.         while (first<=last)
  950.         {
  951.             on = (first+last)/2;
  952.             SETHASBEEN(on,count);
  953.             if (hasBeen) first=on+1;
  954.             else last=on-1;
  955.         }
  956.         if (!hasBeen)
  957.             return(on);
  958.         else
  959.             return(on+1);
  960.     }
  961. }
  962.  
  963. /************************************************************************
  964.  * HasBeenRead - has a particular message been read?
  965.  * look for a "Status:" header; if it's "Status: R<something>", message
  966.  * has been read
  967.  ************************************************************************/
  968. Boolean HasBeenRead(short msgNum,short count)
  969. {
  970.     Str127 scratch;
  971.     Boolean unread=False, statFound=False;
  972.     Str31 terminate;
  973.     Str31 status;
  974.     UPtr cp;
  975.     long size;
  976.  
  977.     if (msgNum>count) return(0);
  978.     Progress((msgNum*100)/count,GetRString(scratch,FIRST_UNREAD));
  979.     Pause(60);
  980.     GetRString(terminate,ALREADY_READ);
  981.     GetRString(status,STATUS);
  982.     NumToString(msgNum,scratch);
  983.     PLCat(scratch,1);
  984.     POPCmd(top,scratch);
  985.     for (size=sizeof(scratch);
  986.                 !(Prr=RecvLine(scratch,&size)) &&
  987.                 !(scratch[0]=='.' && scratch[1]=='\n');
  988.                 size=sizeof(scratch))
  989.         if (!unread && !statFound && !striscmp(scratch,status+1))
  990.         {
  991.             statFound = True;
  992.             for (cp=scratch;cp<scratch+size;cp++)
  993.             {
  994.                 if (*cp==':')
  995.                 {
  996.                     for (cp++;cp<=scratch+size-*terminate;cp++)
  997.                         if (!striscmp(cp,terminate+1)) break;
  998.                     unread = cp> scratch+size-*terminate;
  999.                     break;
  1000.                 }
  1001.             }
  1002.         }
  1003.     return(statFound && !unread);
  1004. }
  1005.  
  1006. /************************************************************************
  1007.  * StampPartNumber - put the part number on a mail message
  1008.  ************************************************************************/
  1009. void StampPartNumber(MSumPtr sum,short part,short count)
  1010. {
  1011.     char *spot;
  1012.     short i, digits, len;
  1013.     
  1014.     for (i=count,digits=0;i;i/=10,digits++);
  1015.     len = 2*digits+2;
  1016.     spot=sum->subj+MIN(*sum->subj+len,sizeof(sum->subj)-1);
  1017.     *sum->subj = spot-sum->subj;
  1018.     for (i=digits;i;i--,count/=10) *spot-- = '0'+count%10;
  1019.     *spot-- = '/';
  1020.     for (i=digits;i;i--,part/=10) *spot-- = '0'+part%10;
  1021.     *spot = ' ';
  1022. }
  1023.     
  1024. /************************************************************************
  1025.  * AddAttachNote - note that we've attached a file
  1026.  ************************************************************************/
  1027. void AddAttachNote(short wdRef, UPtr name, long creator, long type)
  1028. {
  1029.     Str255 fileAndFolder;
  1030.     Str31 folderName;
  1031.     Str15 typeString, creatorString;
  1032.     
  1033.     BlockMove(&creator,creatorString+1,4);
  1034.     BlockMove(&type,typeString+1,4);
  1035.     *creatorString = *typeString = 4;
  1036.     GetDirName(nil,wdRef,GetMyDirID(wdRef),folderName);
  1037.     ComposeRString(fileAndFolder,FILE_FOLDER_FMT,
  1038.                                  folderName,name,typeString,creatorString);         
  1039.     PtrAndHand(fileAndFolder+1,AttachedFiles,*fileAndFolder);
  1040. }
  1041.  
  1042. /************************************************************************
  1043.  * AddAttachInfo - attach a note about problems with the enclosure
  1044.  ************************************************************************/
  1045. void AddAttachInfo(int theIndex, int result)
  1046. {
  1047.     Str255 theMessage;
  1048.     
  1049.     ComposeString(theMessage,"\p%r%d\n", theIndex, result);
  1050.     PtrAndHand(theMessage+1,AttachedFiles,*theMessage);
  1051. }
  1052.  
  1053.  
  1054. #ifdef POPSECURE
  1055. /************************************************************************
  1056.  * VetPOP - make sure the user's POP account is ok.
  1057.  ************************************************************************/
  1058. short VetPOP(void)
  1059. {
  1060.     Str255 user, host;
  1061.     short port;
  1062.     short err;
  1063.     
  1064.     if (UUPCIn && !UUPCOut) {WarnUser(UUPC_SECURE,0);return(1);}
  1065.     GetPOPInfo(user,host);
  1066.     port = GetRLong(POP_PORT);
  1067. #ifdef DEBUG
  1068.     if (BUG12) port=40110;
  1069. #endif DEBUG
  1070.     if ((err=StartPOP(host,port))==noErr)
  1071.     {
  1072.         (void) POPIntroductions();
  1073.         if (Prr) err=Prr;
  1074.     }
  1075.     EndPOP();
  1076.     if (UseCTB && !err) CTBNavigateSTRN(NAVMID);
  1077.     return(err);
  1078. }
  1079. #endif
  1080.  
  1081. #ifndef KERBEROS
  1082. #pragma segment MD5
  1083. /************************************************************************
  1084.  * GenDigest - generate a digest for APOP
  1085.  ************************************************************************/
  1086. Boolean GenDigest(UPtr banner,UPtr secret,UPtr digest)
  1087. {
  1088.     Str255 stamp;
  1089.     MD5_CTX md5;
  1090.     static char hex[]="0123456789abcdef";
  1091.     short i;
  1092.     
  1093.     if (!*ExtractStamp(stamp,banner)) return(False);
  1094.     
  1095.     MD5Init(&md5);
  1096.     MD5Update(&md5,stamp+1,*stamp);
  1097.     MD5Update(&md5,secret+1,*secret);
  1098.     MD5Final(&md5);
  1099.     
  1100.     for (i=0;i<sizeof(md5.digest);i++)
  1101.     {
  1102.         digest[2*i+1] = hex[(md5.digest[i]>>4)&0xf];
  1103.         digest[2*i+2] = hex[md5.digest[i]&0xf];
  1104.     }
  1105.     digest[0] = 2*sizeof(md5.digest);
  1106.     return(True);
  1107. }
  1108.  
  1109. /************************************************************************
  1110.  * ExtractStamp - grab the timestamp out of a POP banner
  1111.  ************************************************************************/
  1112. UPtr ExtractStamp(UPtr stamp,UPtr banner)
  1113. {
  1114.     UPtr cp1,cp2;
  1115.     
  1116.     *stamp = 0;
  1117.     banner[*banner+1] = 0;
  1118.     if (cp1=strchr(banner+1,'<'))
  1119.         if (cp2=strchr(cp1+1,'>'))
  1120.         {
  1121.             *stamp = cp2-cp1+1;
  1122.             strncpy(stamp+1,cp1,*stamp);
  1123.         }
  1124.     return(stamp);
  1125. }
  1126. #endif
  1127.